/*
 * Copyright (c) 2009, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.wso2.carbon.bpel.b4p.extension;

import org.apache.ode.bpel.runtime.extension.AbstractAsyncExtensionOperation;
import org.w3c.dom.Element;
import org.apache.ode.bpel.runtime.extension.ExtensionContext;
import org.apache.ode.bpel.runtime.extension.AbstractSyncExtensionOperation;
import org.apache.ode.bpel.common.FaultException;
import org.apache.ode.bpel.evar.ExternalVariableModuleException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.axis2.AxisFault;
import org.apache.axis2.util.XMLUtils;
import org.apache.axis2.description.AxisService;
import org.wso2.carbon.bpel.b4p.internal.B4PServiceComponent;
import org.wso2.carbon.bpel.b4p.utils.CallbackServiceFactory;

import javax.xml.namespace.QName;
import java.util.concurrent.CountDownLatch;
import java.util.Map;
import java.util.Hashtable;

public class B4PExtensionOperation extends AbstractSyncExtensionOperation {
    protected final Log log = LogFactory.getLog(B4PExtensionOperation.class);

    private ExtensionContext extensionContext;
    private PeopleActivity peopleActivity;
    public static Map<String, CountDownLatch> countDownLatches = new Hashtable<String, CountDownLatch>();
    /*The output Element is written against the Task ID in the HumanMessageReceiver.
    * It is read against the Task ID when the count down latch is continued*/
    public static Map<String, Element> outPutVarElementMap = new Hashtable<String, Element>();
    //public Element outPutVariableVal;

    String taskID;

    public void runSync(ExtensionContext extensionCntext, Element element) throws FaultException {
        extensionContext = extensionCntext;
        if (log.isDebugEnabled()) {
            log.debug("Executing " + extensionContext.getActivityName() + " activity");
        }

        PeopleActivity peopleActivity = new PeopleActivity(extensionContext, element);
        this.peopleActivity = peopleActivity;

        HumanService humanService;
        try {
            humanService = new HumanService(peopleActivity, B4PServiceComponent.getClientConfigurationContext(),
                    B4PServiceComponent.getHttpConnectionManager());
        } catch (AxisFault axisFault) {
            log.error("Error while initializing Human Service: " + peopleActivity.getServiceName(), axisFault);
            throw new FaultException(new QName(B4PExtensionBundle.NS, axisFault.getMessage()), axisFault);
        }

        //org.apache.ode.bpel.iapi.Message response = humanService.invoke();
        if (peopleActivity.getActivityType().equals(InteractionType.TASK)) {
            taskID = humanService.invoke();
            if (taskID == null) {
                log.error("Task ID not found for the Human Task invoked.");
                throw new FaultException(new QName(B4PExtensionBundle.NS, "Task ID not found for the Human Task invoked."));
            }
            if (log.isDebugEnabled()) {
                log.debug(taskID + ": FeedBack Received");
            }
            CountDownLatch cdl = new CountDownLatch(1);
            //countDownLatch = cdl;
            countDownLatches.put(taskID, cdl);

            AxisService callbackService = null;
            try {
                callbackService = CallbackServiceFactory.getCallbackService(peopleActivity.getCallbackServiceName(),
                        peopleActivity.getCallbackServicePort(),
                        B4PServiceComponent.getConfigurationContext().getAxisConfiguration(),
                        peopleActivity.getCallbackWSDL(), peopleActivity.getCallbackOperation());
            } catch (AxisFault axisFault) {
                log.error("The callback service not found for the service: " + peopleActivity.getCallbackServiceName(),
                        axisFault);
                throw new FaultException(new QName(B4PExtensionBundle.NS, axisFault.getMessage()), axisFault);
            }

            if (callbackService == null) {
                log.error("The callback service not found for the service: " + peopleActivity.getCallbackServiceName());
                throw new FaultException(new QName(B4PExtensionBundle.NS, "The callback service not found for the service: "
                        + peopleActivity.getCallbackServiceName()));
            }

            try {
                if (log.isDebugEnabled()) {
                    log.debug(taskID + ": Waiting For the response");
                }
                cdl.await();
                if (log.isDebugEnabled()) {
                    log.debug(taskID + ": Latch is continueing");
                }
            } catch (InterruptedException e) {
                log.error("The thread is interrupted. TaskID: " + taskID, e);
                throw new FaultException(new QName(B4PExtensionBundle.NS, e.getMessage()), e);
            }

            writeOutputVariable();
        } else {
            humanService.invoke();
        }
    }

    private void writeOutputVariable() throws FaultException {
        Element outPutVariableVal = outPutVarElementMap.remove(taskID);
            if (outPutVariableVal == null){
                log.warn("The value of " + peopleActivity.getOutputVarName() + " is null");
            }
            extensionContext.writeVariable(peopleActivity.getOutputVarName(), outPutVariableVal);
    }
}